package ch.exm.storm.query;

import java.util.List;
import java.util.Set;

import ch.exm.storm.conditions.And;
import ch.exm.storm.conditions.Condition;
import ch.exm.storm.query.selection.Selection;
import ch.exm.storm.query.selection.Selection.Aggregation;

public abstract class AbstractQuery implements Query {

	protected QueryPlan queryPlan;
	
	public Query clear() {
		selection = null;
		conditions = new And();
		queryPlan = null;
		return this;
	}
	
	public QueryPlan getQueryPlan() {
		if (queryPlan == null) evaluateQueryPlan();
		return queryPlan;
	}

	protected abstract void evaluateQueryPlan();
	
	@SuppressWarnings("unchecked")
	public final <T> List<T> doQuery() {
		return (List<T>)getQueryPlan().execute();
	}
	
	protected Set<Class<?>> getQueriedClasses() {
		Set<Class<?>> queriedClasses = conditions.getQueriedClasses(); 
		if (queriedClasses.isEmpty() && selection != null) queriedClasses.add(selection.getSelectedClass());
		return queriedClasses;
	}
	
	// ////////////////////////////////////////////////////////////////////////
	// /// Where
	// ////////////////////////////////////////////////////////////////////////
	
	protected And conditions = new And();
	
	public Query where(Condition condition) {
		conditions.addCondition(condition);
		return this;
	}
	
	// ////////////////////////////////////////////////////////////////////////
	// /// Select
	// ////////////////////////////////////////////////////////////////////////
	
	protected Selection selection;
	
	public Query select(Object selected) {
		return select(selected, false);
	}
	
	public Query selectDistinct(Object selected) {
		return select(selected, true);
	}

	public Query select(Object selected, boolean distinct) {
		selection = new Selection(selected, distinct);
		return this;
	}

	public Query select(Object selected, Aggregation aggregation) {
		selection = new Selection(selected, aggregation);
		return this;
	}

	public Query selectAvg(Object selected) {
		return select(selected, Selection.Aggregation.AVG);
	}
	public Query selectSum(Object selected) {
		return select(selected, Selection.Aggregation.SUM);
	}
	public Query selectMin(Object selected) {
		return select(selected, Selection.Aggregation.MIN);
	}
	public Query selectMax(Object selected) {
		return select(selected, Selection.Aggregation.MAX);
	}
	public Query selectCount(Object selected) {
		return select(selected, Selection.Aggregation.COUNT);
	}
}
